package org.datascooter.db.db2;

import java.text.MessageFormat;
import java.util.List;

import org.datascooter.bundle.DSMapper;
import org.datascooter.bundle.EntityBundle;
import org.datascooter.bundle.attribute.PersistAttribute;
import org.datascooter.bundle.attribute.PersistReferenceAttribute;
import org.datascooter.exception.EntityNotMappedException;
import org.datascooter.impl.DataSnip;
import org.datascooter.impl.Snip;
import org.datascooter.impl.SnipBuilder;
import org.datascooter.inface.ISnipBuilder;
import org.datascooter.meta.Column;
import org.datascooter.utils.DBType;
import org.datascooter.utils.SnipUtils;
import org.datascooter.utils.policy.SnipType;

public class Db2SnipBuilder extends SnipBuilder {

	public Db2SnipBuilder() {
		super("db2");
	}

	@Override
	public ISnipBuilder copy() {
		return new Db2SnipBuilder();
	}

	@Override
	public String managePage(DataSnip dataSnip, String sqlClause) {
		if (dataSnip.fetchSize > 0) {
			managePageParams(dataSnip, params);
			sqlClause = sqlClause.replace("FROM", ", rownumber()  OVER(ORDER BY " + dataSnip.bundle.id.getColumnName() + ") AS ROW_NEXT  FROM");
			return "SELECT * FROM  (" + sqlClause + ") X WHERE X.ROW_NEXT BETWEEN  ?  AND  ?";
		}
		return sqlClause;
	}

	@Override
	public void managePageParams(DataSnip dataSnip, List<Object> paramList) {
		paramList.add((Integer) dataSnip.fromRow);
		paramList.add(dataSnip.fromRow + dataSnip.fetchSize - 1);
	}

	@SuppressWarnings("nls")
	@Override
	public String buildColumnClause(PersistAttribute attribute) {
		String typeStr = getDbTypeString(attribute.getType());
		String size = buildDataLength(attribute.getScale(), attribute.getPrecision());
		DBType type = attribute.getType();
		switch (type) {
			case CHAR:
				typeStr = SnipUtils.CHAR;
				if (size.length() == 0) {
					size = "(255)";
				}
				break;
			case BIGDECIMAL:
				typeStr = "DECIMAL";
				size = "(22,2)";
				break;
			case LONG:
				typeStr = "BIGINT";
				size = "";
				break;
			case BOOLEAN:
				typeStr = "SMALLINT";
				size = "";
				break;
			case INT:
				typeStr = "INTEGER";
				size = "";
				break;
			case STRING:
				typeStr = "VARCHAR";
				if (size.length() == 0) {
					size = "(255)";
				}
				break;
			case BLOB:
				typeStr = "BLOB";
				if ((size != null) && (size.length() > 0)) {
					size = "(" + size + " M )   LOGGED  COMPACT ";
				} else {
					size = "(1 M )   LOGGED  COMPACT ";
				}
				break;
			case TIMESTAMP:
				typeStr = "TIMESTAMP";
				break;
		}
		return SnipUtils.SP + attribute.getColumnName() + SnipUtils.SP + typeStr + SnipUtils.SP + size + (attribute.isNullable() ? SnipUtils.NN : SnipUtils.NOT_NULL);
	}

	@Override
	public boolean equalType(PersistAttribute attribute, Column column) {
		boolean result = false;
		boolean scaled = true;
		if (attribute.getType() == null) {
			return true;
		}
		switch (attribute.getType()) {
			case BIGDECIMAL:
				result = "DECIMAL".equalsIgnoreCase(column.dataTypeName);
				break;
			case LONG:
				result = "BIGINT".equalsIgnoreCase(column.dataTypeName);
				scaled = false;
				break;
			case BOOLEAN:
				result = "SMALLINT".equalsIgnoreCase(column.dataTypeName);
				break;
			case INT:
				result = "INTEGER".equalsIgnoreCase(column.dataTypeName);
				scaled = false;
				break;
			case STRING:
				result = "VARCHAR".equalsIgnoreCase(column.dataTypeName);
				break;
			case CHAR:
				result = "CHAR".equalsIgnoreCase(column.dataTypeName);
				break;
			case BLOB:
				result = "BLOB".equalsIgnoreCase(column.dataTypeName);
				break;
			case CLOB:
				result = "CLOB".equalsIgnoreCase(column.dataTypeName);
				break;
			case TIMESTAMP:
				result = "DATE".equalsIgnoreCase(column.dataTypeName) || "TIMESTAMP".equalsIgnoreCase(column.dataTypeName);
				scaled = false;
				break;
		}
		result = checkScale(attribute, column, result, scaled);
		return result;
	}

	@Override
	public Snip buildUpdateColumnSnip(String entity, PersistAttribute attribute) throws EntityNotMappedException, SecurityException, NoSuchMethodException {
		EntityBundle bundle = DSMapper.getBundle(entity);
		initBuilders();
		return new Snip(null, SnipType.EXECUTE, MessageFormat.format(SnipUtils.ALTER_TABLE_CHANGE, bundle.getTableName(), buildDB2ColumnClause(bundle, attribute)), null, null, null, -1, null);
	}

	private String buildDB2ColumnClause(EntityBundle bundle, PersistAttribute attribute) {
		DBType type;
		String typeStr = "";
		String size = "";
		if (attribute instanceof PersistReferenceAttribute) {
			type = bundle.id.getType();
			size = buildDataLength(bundle.id.getScale(), bundle.id.getPrecision());
		} else {
			type = attribute.getType();
			size = buildDataLength(attribute.getScale(), attribute.getPrecision());
		}
		switch (type) {
			case CHAR:
				typeStr = SnipUtils.CHAR;
				if (size.length() == 0) {
					size = "(255)";
				}
				break;
			case BIGDECIMAL:
				typeStr = "DECIMAL";
				size = "(22,2)";
				break;
			case LONG:
				typeStr = "BIGINT";
				size = "";
				break;
			case BOOLEAN:
				typeStr = "SMALLINT";
				size = "";
				break;
			case INT:
				typeStr = "INTEGER";
				size = "";
				break;
			case STRING:
				typeStr = "VARCHAR";
				if (size.length() == 0) {
					size = "(255)";
				}
				break;
			case BLOB:
				typeStr = "BLOB";
				if ((size != null) && (size.length() > 0)) {
					size = "(" + size + " M )   LOGGED  COMPACT ";
				} else {
					size = "(1 M )   LOGGED  COMPACT ";
				}
				break;
			case TIMESTAMP:
				typeStr = "TIMESTAMP";
				break;
		}
		return SnipUtils.SP + attribute.getColumnName() + SnipUtils.SP + "SET DATA TYPE" + SnipUtils.SP + typeStr + SnipUtils.SP + size + (attribute.isNullable() ? SnipUtils.NN : SnipUtils.NOT_NULL);
	}
}
